package client;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JButton;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {


    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    Client window = new Client();
                    window.frame.setVisible(true);
                    window.runInstruct("ls");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private JFrame frame;
    private JTextField textField;
    private JLabel resultLabel;
    private JTextArea clientArea;
    private JTextArea serverArea;
    /**
     * 默认路径
     */
    private String defaultpath="C:/";
    /**
     * 用户当前路径
     */
    private String path=defaultpath;
    private Socket socket;
    /**
     * 默认字体
     */
    private Font defaultFont=new Font("楷体",Font.PLAIN,40);
    private InputStream istream;
    private OutputStream ostream;
    private BufferedReader in;
    private BufferedWriter out;
    /**
     * 是否登录
     */
    private boolean login=false;
    /**
     * Create the application.
     * @throws IOException
     * @throws UnknownHostException
     */
    public Client() throws UnknownHostException, IOException {
        initialize();
        socket=new Socket("127.0.0.1",6666);
        istream=socket.getInputStream();
        ostream=socket.getOutputStream();
        in = new BufferedReader(new InputStreamReader(istream));
        out=new BufferedWriter(new OutputStreamWriter(ostream));
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(200, 200, 2000, 1300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(null);
        frame.setTitle("输入help查看指令");
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                send("close");
                System.exit(0);
            }
        });
        clientArea = new JTextArea();
        clientArea.setBounds(0, 100, 900, 600);
        JScrollPane clientScrollPane=new JScrollPane(clientArea);
        clientScrollPane.setBounds(0, 100, 900, 600);
        frame.getContentPane().add(clientScrollPane);

        serverArea = new JTextArea();
        serverArea.setBounds(1100, 100, 900, 600);
        serverArea.setFont(defaultFont);
        serverArea.setText("\n\n\n\n\n\n\t\t  [空]");
        JScrollPane serverScrollPane=new JScrollPane(serverArea);
        serverScrollPane.setBounds(1100, 100, 900, 600);
        frame.getContentPane().add(serverScrollPane);

        resultLabel = new JLabel("result");
        resultLabel.setFont(defaultFont);
        resultLabel.setBounds(0, 750, 2000, 100);
        frame.getContentPane().add(resultLabel);

        textField = new JTextField();
        //回车键事件监听
        textField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_ENTER) {
                    try {
                        runInstruct(textField.getText());
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                }
            }
        });
        textField.setFocusable(true);
        textField.setBounds(0, 900, 2000, 100);
        textField.setFont(defaultFont);
        frame.getContentPane().add(textField);
        textField.setColumns(10);

        JButton sendButton = new JButton("Send");
        sendButton.setBounds(1800, 1100, 200, 100);
        //按钮事件监听
        sendButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                try {
                    runInstruct(textField.getText());
                } catch (IOException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
            }
        });
        frame.getContentPane().add(sendButton);

        JLabel label = new JLabel("客户端文件");
        label.setBounds(300, 20, 300, 50);
        label.setFont(defaultFont);
        frame.getContentPane().add(label);

        JLabel label_1 = new JLabel("服务器文件");
        label_1.setBounds(1400, 20, 300, 50);
        label_1.setFont(defaultFont);
        frame.getContentPane().add(label_1);

        JLabel label_2 = new JLabel(">>>");
        label_2.setBounds(970, 300, 100, 100);
        label_2.setFont(defaultFont);
        frame.getContentPane().add(label_2);

        JLabel label_3 = new JLabel("<<<");
        label_3.setBounds(970, 370, 100, 100);
        label_3.setFont(defaultFont);
        frame.getContentPane().add(label_3);
    }

    /**
     * 执行用户指令
     * @param instruct
     * @throws IOException
     */
    private void runInstruct(String instruct) throws IOException {
        textField.setText("");
        String[] parts = instruct.split(" ");
        String line;
        String[] addr;
        switch(parts[0]) {
            case "help":
                clientArea.setFont(defaultFont);
                clientArea.setText("");
                clientArea.append("signup 用户名 密码   注册账号\n");
                clientArea.append("login 用户名 密码   登录账号\n");
                clientArea.append("ls/cls 展示客户端当前目录文件列表\n");
                clientArea.append("cd/ccd 展示客户端目录跳转\n");
                clientArea.append("pwd/cpwd 展示当前客户端路径\n");
                clientArea.append("mkdir/cmkdir 在客户端创建文件夹\n");
                clientArea.append("sls 展示服务器端当前目录文件列表\n");
                clientArea.append("scd 展示服务器目录跳转\n");
                clientArea.append("spwd 展示当前服务器端路径\n");
                clientArea.append("smkdir 在服务器端创建文件夹\n");
                clientArea.append("put 文件名   上传文件\n");
                clientArea.append("get 文件名   下载文件\n");
                break;
            case "ls": case "cls":
                if(path.endsWith("/")) {
                    showFiles(path,clientArea);
                } else {
                    showFiles(path+"/",clientArea);
                }
                break;
            case "sls":
                if(login==false) {
                    resultLabel.setText("请先登录");
                    return;
                }
                send("sls");
                break;
            case "cd": case "ccd":
                if(parts.length==1) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                String[] splits = parts[1].split("/");
                if(parts[1].startsWith("c:")||parts[1].startsWith("C:")
                        ||parts[1].startsWith("d:")||parts[1].startsWith("D:")
                        ||parts[1].startsWith("e:")||parts[1].startsWith("E:")
                        ||parts[1].startsWith("f:")||parts[1].startsWith("F:")
                        ||parts[1].startsWith("g:")||parts[1].startsWith("G:")) {
                    if(new File(parts[1]).exists()==false) {
                        resultLabel.setText("路径不存在"+parts[1]);
                        return;
                    }
                    path=parts[1];
                    showFiles(path,clientArea);
                    return;
                }else if(!splits[0].equals("..")){
                    StringBuilder sb=new StringBuilder(path);
                    for(String tmp:splits) {
                        sb.append(tmp+"/");
                    }
                    String tmppath=sb.toString();
                    if(new File(tmppath).exists()==false) {
                        resultLabel.setText("路径不存在"+tmppath);
                        return;
                    }else {
                        path=tmppath;
                        showFiles(path,clientArea);
                    }
                }else {
                    String[] ps = path.split("/");
                    if(ps.length<splits.length) {
                        path=ps[0]+"/";
                        showFiles(path,clientArea);
                        return;
                    }
                    StringBuilder sb=new StringBuilder(ps[0]+"/");
                    for(int i=1;i<ps.length-splits.length;i++) {
                        sb.append(ps[i]+"/");
                    }
                    path=sb.toString();
                    showFiles(path, clientArea);
                }
                break;
            case "scd":
                send(instruct);
                send("sls");
                break;
            case "pwd":
            case "cpwd":
                resultLabel.setText("客户端当前路径:"+path);
                break;
            case "spwd":
                resultLabel.setText("服务器当前路径:"+send("spwd"));
                break;
            case "login":
                if(parts.length<3) {
                    resultLabel.setText("请按照格式输入用户名和命名   格式:login 用户名 密码");
                    return;
                }
                line=send(instruct);
                resultLabel.setText(line);
                if(line.startsWith("ERROR")) {
                    return;
                }
                send("sls");
                login=true;
                break;
            case "signup":
                if(parts.length<3) {
                    resultLabel.setText("请按照格式输入用户名和命名   格式:signup 用户名 密码");
                    return;
                }
                line=send(instruct);
                resultLabel.setText(line);
                if(line.startsWith("ERROR")) {
                    return;
                }
                login=true;
                send("sls");
                break;
            case "mkdir":
            case "cmkdir":
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                if(new File(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1])).mkdirs()==false) {
                    resultLabel.setText("目录创建失败,路径："+path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]));
                    return;
                }
                runInstruct("ls");
                resultLabel.setText("创建成功");
                break;
            case "smkdir":
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                line=send(instruct);
                send("sls");
                resultLabel.setText(line);
                break;
            case "rmdir":
            case "crmdir":
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                if(new File(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1])).exists()==false) {
                    resultLabel.setText("目录不存在,路径："+path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]));
                    return;
                }
                if(new File(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1])).delete()==false) {
                    resultLabel.setText("目录删除失败,路径："+path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]));
                    return;
                }
                runInstruct("ls");
                resultLabel.setText("删除成功");
                break;
            case "srmdir":
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                line=send(instruct);
                send("sls");
                resultLabel.setText(line);
                break;
            case "put":
                //未输入路径
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                File file=new File(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]));
                //文件不存在
                if(file.exists()==false) {
                    resultLabel.setText("文件"+(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1])+"不存在"));
                    return;
                }
                if(file.isDirectory()) {
                    resultLabel.setText("文件"+(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1])+"为文件夹，不能上传"));
                    return;
                }
                line=send(instruct);
                //系统空间不够
                if(file.length()>Long.parseLong(line)) {
                    resultLabel.setText("文件太大，服务器无法储存，文件大小"+new File(path).length()+" 系统剩余"+Long.parseLong(line));
                    out.write("ERROR");
                    out.newLine();
                    out.flush();
                    return;
                }
                resultLabel.setText("文件大小"+file.length()+"开始发送....");
                //告诉系统空间足够
                out.write("enough");
                out.newLine();
                out.flush();
                //读取IP和端口号
                line=in.readLine();
                addr = line.split(" ");
                new PutFileThread(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]),new Socket(addr[0],Integer.parseInt(addr[1]))).start();
                break;
            case "get":
                if(parts.length<2) {
                    resultLabel.setText("请输入路径");
                    return;
                }
                line=send(instruct);
                //遇到错误
                if(line.startsWith("ERROR")) {
                    resultLabel.setText(line);
                    return;
                }
                //判断系统客户端空间是否够用
                if(new File(path).getFreeSpace()<Long.parseLong(line)) {
                    resultLabel.setText("系统硬盘大小不够");
                    out.write("ERROR");
                    out.newLine();
                    out.flush();
                    return;
                }
                //提示服务器硬盘大小足够
                out.write("enough");
                out.newLine();
                out.flush();
                resultLabel.setText("文件大小:"+(Long.parseLong(line)/1024.0/1024)+"M,开始接收....");
                //接收IP和端口
                line=in.readLine();
                if(line.startsWith("ERROR")) {
                    resultLabel.setText(line);
                    return;
                }
                addr = line.split(" ");
                new GetFileThread(path+(parts[1].startsWith("/")?parts[1].substring(1,parts[1].length()):parts[1]),new Socket(addr[0],Integer.parseInt(addr[1]))).start();
                break;
            default:
                resultLabel.setText("指令不存在:"+instruct);
        }
    }



    /**
     * 向服务器发送指令并阻塞等待结果
     * @param data
     * @return
     */
    private String send(String data) {
        String line=null;
        try {
            out.write("#INSTRUCT#"+data);
            out.newLine();
            out.flush();
            if(data.equals("sls")) {
                int size=0;
                serverArea.setText("");
                while(!(line=in.readLine()).equals("#END#")) {
                    size++;
                    serverArea.append(line+"\n");
                }
                if(size==0) {
                    serverArea.setText("\n\n\n\n\n\n\t\t  [空]");
                    resultLabel.setText("共"+size+"个文件");
                }else {
                    resultLabel.setText("共"+size+"个文件");
                }
                return null;
            }else {
                line = in.readLine();
                return line;
            }

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return line;
    }

    /**
     * 发送文件线程
     * @author Administrator
     *
     */
    class PutFileThread extends Thread{
        BufferedInputStream bis;
        BufferedOutputStream bos;
        public PutFileThread(String path,Socket sendSocket) throws IOException {
            bis=new BufferedInputStream(new FileInputStream(new File(path)));
            bos=new BufferedOutputStream(sendSocket.getOutputStream());
        }

        @Override
        public void run() {
            byte[] buff=new byte[1024];
            int len=0;
            try {
                while((len=bis.read(buff))!=-1) {
                    bos.write(buff, 0, len);
                }
                bos.flush();
                runInstruct("sls");
                resultLabel.setText("上传成功");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    bis.close();
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 接收文件线程
     * @author Administrator
     *
     */
    class GetFileThread extends Thread{
        BufferedInputStream bis;
        BufferedOutputStream bos;
        String path;
        public GetFileThread(String path,Socket getSocket) throws IOException {
            bis=new BufferedInputStream(getSocket.getInputStream());
            bos=new BufferedOutputStream(new FileOutputStream(new File(path)));
            this.path=path;
        }

        @Override
        public void run() {
            byte[] buff=new byte[1024];
            int len=0;
            try {
                while((len=bis.read(buff))!=-1) {
                    bos.write(buff, 0, len);
                }
                bos.flush();
                runInstruct("ls");
                resultLabel.setText("下载成功");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                try {
                    bis.close();
                    bos.close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

            }
        }
    }

    /**
     * 显示指定路径下的文件列表
     * @param path
     * @param area
     */
    private void showFiles(String path,JTextArea area) {
        File file = new File(path);
        File[] listFiles = file.listFiles();
        resultLabel.setText("");
        area.setText("");
        area.setFont(defaultFont);
        int size=0;
        for(File tmp:listFiles) {
            area.append(tmp.getName()+"\n");
            size++;
        }
        resultLabel.setText("共"+size+"个文件");
        if(size==0) {
            area.setText("\n\n\n\n\n\n\t\t  [空]");
        }
    }
}
